iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
1
自我挑戰組

30天找回寫程式手感計劃!!!系列 第 27

Day27 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day3

  • 分享至 

  • xImage
  •  

在寫這一篇的時候,
看到右邊 iT邦幫忙鐵人賽 跳出的話,
https://ithelp.ithome.com.tw/upload/images/20201002/20129873W9IPS1hHRM.png

可能你沒有他人的天賦,但你沒有理由比別人不努力。

是啊,正因為我不是天才,所以才更應該要努力不是嗎?
(雖然我自己知道我很不認真就是了XD")

正片開始

今日課題:戰鬥選單

今天要主攻戰鬥選單,
https://ithelp.ithome.com.tw/upload/images/20201002/20129873paGQDb6rcP.png
大家應該會注意到,
當該選項被選擇的時候,
是會顯示土黃色的。
那要怎麼做呢?
總不能將被選擇的樣子截圖下來,
用圖片交換吧?
這樣就太 low 了XD

對,所以我們應該是該選項被選到的時候在其上面疊上土黃色色塊(遮罩?)才對。
然後選項看起來是正八邊形,
所以我們要試圖弄出正八邊形才行。
話說我相信這是高中數學的程度,
但小的高中數學奇差無比,
然後去年遇到這個問題的時候,
想說「欠的果然都是要還的」orz
那時候沒學好的高中數學果然還是回來找我了啊啊啊啊啊~~~~~(吶喊)
為了這個我甚至跑去買高中數學的三角函數來 K,
(因為好像邊長、角度的關係公式是三角函數的東西?)
反正到最後我其實還是沒搞懂三角函數orz
我到 codepen, Google 上找尋各位大大用正X邊形是怎麼弄出來的,
然後再自己依據比例原則 tune,
終於 tune 出像樣的東西orz

因此這邊原諒小的不說明這邊的 CSS,
因為老實說我也不太知道怎麼用出來的QQ

這邊直接提供我的做法,
html 要加上 mask 的物件:

<div class="option skill active">
    <div class="mask"></div>
</div>

CSS 看起來會用到偽元素:

.active{
    .mask{
        position:relative;
        opacity: 0.35;

        width:95px;
        height:0;
        border-width:0 25px 25px 25px;
        border-style:solid;
        border-color:transparent transparent  #846c4f;
        &:before{
            position:absolute;
            content:"";
            top:70px;
            left:-25px;
            width:95px;
            height:0;
            border-width:25px 25px 0 25px;
            border-style:solid;    
            border-color: #846c4f transparent transparent;
        }
        &:after{
            position:absolute;
            content:"";
            top:25px;
            left:-25px;
            width:95px;
            height:0;
            border-width:0 0 45px 0;
            background:none;
            border-style:solid;
            border-color:transparent transparent #846c4f;
        }
    }
}

讓我們看看效果吧!
https://ithelp.ithome.com.tw/upload/images/20201002/2012987393FohrfVb3.png
唷,看起來很棒!
去年的我是被這篇所救 → 單一 div 的正多邊形變換 ( 純 CSS ) | OXXO.STUDIO
有大大的詳細講解,
小的在這邊跟大大鞠躬道謝 m(_ _)m

被選到的選項才要加土黃色遮罩

上面弄出我們要的樣子後,
接下來就是要判斷,被選到的選項才要加土黃色遮罩,
而且預設選項就是普通攻擊。

這邊我們先試一個東西,
就是控制物件的 class 要加上 active,
因為我們的 CSS 是將 mask 訂在 active 底下,
因此 mask 的父容器 class 有 active,
mask 才會顯示出來。

html 結構是這樣:

<div class="war_menu">
    <div class="option skill">
        <div class="mask"></div>
    </div>
    <div class="center">
        <div class="option attack">
            <div class="mask"></div>
        </div>
        <div class="option item">
            <div class="mask"></div>
        </div>
    </div>
    <div class="option cooperation">
        <div class="mask"></div>
    </div>
</div>

這邊我先在 war_menu 的物件埋一個 data-option 的屬性,
來存放現在選到的選項是什麼,
再來 JavaScript 就可以用 物件.dataset.option 來取得 data-option 裡面的值。

html

<div class="war_menu" data-option="skill"></div>

JavaScript

const warMenuElement = document.querySelector(".war_menu");
console.log(`現在的選項是:${warMenuElement.dataset.option}`);

https://ithelp.ithome.com.tw/upload/images/20201003/201298730wTQogtKhc.png

然後因為之後 data-option 要隨著不同的選項被選到,值的內容也要跟著變動,
所以這邊也試一下可不可以改 data-option 的值。

warMenuElement.setAttribute("data-option","cooperation");
console.log(`現在的選項是:${warMenuElement.dataset.option}`);

https://ithelp.ithome.com.tw/upload/images/20201003/20129873rcAmhfZ4fb.png
看起來也 OK!
我們的最終目標是,拿 data-option 的值 比對 class,
假設 data-option 的值為 cooperation,
比對戰鬥選單的 4 個選項的 class,
如果有比對到 cooperation,
就在它的 class 後面加上 active。
因此這邊就要用到正規表示式了,
這邊用的語法是 patten.test("要比對的字串")
回傳 true/false 這樣。

還有因為選項有 4 個,不想寫 4 行 document.querySelector
可以使用 document.querySelectorAll,它會將相同 class 的物件都抓出來,成為陣列。
再來獲得屬性的語法是 物件.getAttribute("屬性名稱")
設定屬性的語法是 物件.setAttribute("屬性名稱","值")

// 正規表示式的 Pattern 設為現在被選到的選項
let pattCurrentOption = new RegExp(warMenuElement.dataset.option);

const warMenuOptionElement = document.querySelectorAll(".war_menu .option");
for ( let i=0; i<warMenuOptionElement.length; i++){
    console.log(`現在的 class 是:${warMenuOptionElement[i].getAttribute("class")}`);
    let result = pattCurrentOption.test(warMenuOptionElement[i].getAttribute("class"));
    console.log(`比對結果:${result}`);
    // 如果有比對到的,該物件 class 加上 active
    if ( result ){
        warMenuOptionElement[i].setAttribute("class", `${warMenuOptionElement[i].getAttribute("class")} active`);
    }
}

前面我們設定 data-option 的值為 cooperation,
因此最後我們可以看到 cooperation 有土黃色遮罩了!
https://ithelp.ithome.com.tw/upload/images/20201003/20129873Zt2W6Ck1kZ.png

偵測鍵盤事件,讓玩家可以自己選選項

再來還有一件事要搞定,
要讓玩家可以自行選選項,
按照原版方式是要以按鍵盤方式,
所以要偵測鍵盤事件,
而且要偵測上下左右鍵。

鍵盤事件不是針對單一物件的監聽,
所以要以整個視窗為監聽對象。

window.addEventListener("keydown", optionSelect);
function optionSelect(event){
    console.log(`keyCode: ${event.keyCode}`);
}

https://ithelp.ithome.com.tw/upload/images/20201003/20129873abDE9Z2ZlB.png
keyCode 就是按下哪個鍵的數值,
但你會說我怎麼知道數值是對應到哪個鍵呢?
JavaScript Event KeyCodes
這個網站有按鍵跟值的對應,
像我按下 Ctrl 鍵就會顯示其對應的 keyCode 為 17。
https://ithelp.ithome.com.tw/upload/images/20201003/20129873gvLOO6aJ5S.png

再來我們剖析一下停在每個選項所可以按的鍵:
https://ithelp.ithome.com.tw/upload/images/20201003/20129873hxQgGBGCwI.png

然後我們用 switch case 判斷現在所按下的鍵,
比如說點擊往下鍵,
攻擊就要有土黃色遮罩。

window.addEventListener("keydown", optionSelect);
function optionSelect(event){
    console.log(`keyCode: ${event.keyCode}`);
    let currentOption = warMenuElement.dataset.option;
    switch(event.keyCode){
        case 38:
            console.log("往上鍵 有被點擊到");
            console.log(`currentOption: ${currentOption}`);
            if ( currentOption === "skill" ){
                warMenuElement.setAttribute("data-option","attack");
                setActiveClass();
            }
            break;
        default:
            console.log("default");
            break;
    }
}

https://ithelp.ithome.com.tw/upload/images/20201003/20129873mIXtUGSYdv.png
看來是有了,
但原本選項的 active 要移掉囧
這次用正規表示式的 replace 語法來試試。

for ( let i=0; i<warMenuOptionElement.length; i++){
    let optionClass = warMenuOptionElement[i].getAttribute("class");
    let result = pattCurrentOption.test(warMenuOptionElement[i].getAttribute("class"));
    // 如果有比對到的,該物件 class 加上 active
    // 沒有的話,該物件 class 移除 active
    if ( result ){
        warMenuOptionElement[i].setAttribute("class", `${optionClass} active`);
    } else{
        optionClass = optionClass.replace("active","");
        warMenuOptionElement[i].setAttribute("class",optionClass);
    }
}

看來成功了!
https://ithelp.ithome.com.tw/upload/images/20201003/2012987341SWF2ejeH.png
(PS. 這時候我就很懷念 jQuery 的 toggleClass 功能,
物件.toggleClass("active") => 它會自己去看該物件的 class 有沒有 active,
有就移除 active,沒有就加上 active,
超方便好用,
但想說這次就不要用到 jQuery 了,用原生語法拼出來QQ)

好,中間支線解決了,
再來就是將每個鍵盤事件都定義好。

window.addEventListener("keydown", optionSelect);
function optionSelect(event){
    switch(event.keyCode){
        case 38: // 上鍵
            warMenuElement.setAttribute("data-option","attack");
            break;
        case 40: // 下鍵
            warMenuElement.setAttribute("data-option","item");
            break;
        case 37: // 左鍵
            warMenuElement.setAttribute("data-option","skill");
            break;
        case 39: // 右鍵
            warMenuElement.setAttribute("data-option","cooperation");
            break;
        default:
            console.log("default");
            break;
    }
    setActiveClass(); // 是現在所點的選項就加上土黃色遮罩
}

https://ithelp.ithome.com.tw/upload/images/20201003/201298733a52RVXLDD.png
https://ithelp.ithome.com.tw/upload/images/20201003/20129873WRfDlpu0MP.png
看來很棒,根據我點選上下左右土黃色遮罩也會跟著移動。

但是其實有一個 bug,
就是如果我現在向下鍵故意按 2 次,
再點選其他鍵:
https://ithelp.ithome.com.tw/upload/images/20201003/20129873zduwoHeCwq.png
就會看到原本的選項土黃色遮罩沒有被移除掉,
看一下 console.log:
https://ithelp.ithome.com.tw/upload/images/20201003/20129873xfzlsyLB60.png
原因是因為原本選項是 item,
但我又點選向下鍵 2 次,
item 的 class 被疊加 2 次 active,
所以我的做法是在加 active 之前先看看 class 是否已包含 active,
沒有的話才加上 active。

let pattActiveOption = new RegExp("active","g");
if ( result ){
    if ( !pattActiveOption.test(optionClass) ){ // class 沒有 active 才能加 active
        warMenuOptionElement[i].setAttribute("class", `${optionClass} active`);
    }
}

https://ithelp.ithome.com.tw/upload/images/20201003/20129873mvamuWDf1K.png
看來正常了!
沒想到用戰鬥選單花這麼多時間XD
那本日收工XD

[後記]

明天應該就要進入回合制的設定,
真正的硬仗orz

但先看一下這三天的成果,
https://ithelp.ithome.com.tw/upload/images/20201003/20129873lCpCJfhw9z.png
BGM 再自己配上戰鬥曲→ 仙劍奇俠傳 - 風起雲湧 - Battle Theme - BGM
整個超有 fu!
頑張!!!!!


上一篇
Day26 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day2
下一篇
Day28 - 「登愣登愣,登愣登登登」~ 隱挑戰 Day4
系列文
30天找回寫程式手感計劃!!!36
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言